--[[
each record representation one request/response
--]]
local specification = require('specification')
local myutils = require('myutils')
local struct = require('struct')
local structMapper = {
  ending = '>',
  int = "i",
  long = "l",
  buffer = "S",
  boolean = "B",
  ustring = "S"
}

local isDebug = true
local _M = {}
_M.__index = _M

-- 4 byte -> int valuve
function _M.unpackInt(byte4)
  local obj = struct.unpack('>i', byte4)
--  print(type(obj))
--  print(myutils.printObj(obj))
  return obj[1]
  -- return obj
end

function _M:getIntValue()
  local byte4 = string.sub(self.stream, self.offset, self.offset + 4 - 1)
  self.offset = self.offset + 4
  local obj = struct.unpack('>i', byte4)
  return obj[1]
end

function _M:getStructLen(type)
  if type == "int" then return 4 end
  if type == "long" then return 8 end
  if type == "boolean" then return 1 end

  if type == "buffer" then
    return self:getIntValue()
  end

  if type == "ustring" then
    return self:getIntValue()
  end
end


function _M:new(protocol, stream)
  local response = {
    protocol = protocol,
    stream = stream,
    offset = 1
  }
  return setmetatable(response, self)
end

--
function _M:setStream(stream)
  self.stream = stream
end

function simpleMapperVerse(type, value)
  return struct.unpack(type, value)
end

function _M:showInfo()
  -- header
  if self.header ~= nil then
    print('------header------')
    print(myutils.printObj(self.header))
  end
  -- payload
  print('------payload------')
  print(myutils.printObj(self.payload))
end



-- parse receive data
function _M:parse()
  -- parse header
  -- parse payload
  if self.protocol ~= 'ConnectResponse' then
    self:parseHeader()
  end
  -- TODO
  if self.header then
--    print('head not null')
  end
  --  if self.header and self.header.err ~= 0 then
  --    error("parse header not zero")
  --  end
  self:parsePayload()
end

function _M:errorInfo()
  return self.header.err
end

--[[
    ReplyHeader = {
        { name = "xid", type = "int" },
        { name = "zxid", type = "long" },
        { name = "err", type = "int" }
    }
--]]
function _M:parseHeader()
--  print('parseHeader, offset=' .. self.offset)
  self.header = {}
  local xid = string.sub(self.stream, self.offset, self.offset + 8 - 1)
  self.offset = self.offset + 4

  local zxid = string.sub(self.stream, self.offset, self.offset + 16 - 1)
  self.offset = self.offset + 8

  local err = string.sub(self.stream, self.offset, self.offset + 8 - 1)
  self.offset = self.offset + 4

  self.header = {
    xid = simpleMapperVerse('>i', xid),
    zxid = simpleMapperVerse('>l', zxid),
    err = simpleMapperVerse('>i', err),
  }
end

function _M:parsePayload()
--  print('parsePayload, offset=' .. self.offset)
  local tmp = {}
  local protocol = specification.protocol[self.protocol] -- table { {name=xx, type=xx}, {..}}4

  for _, nameType in pairs(protocol) do
    local name = nameType.name
    local pType = nameType.type
--    print('parse name=' .. name .. ',type=' .. pType)

    if pType == 'int' or pType == 'long' or pType == 'boolean' then
      local len = self:getStructLen(pType)
--      print('len=' .. len .. ',offset = ' .. self.offset)
      local bytes = string.sub(self.stream, self.offset, self.offset + len - 1)
      tmp[name] = simpleMapperVerse('>' .. structMapper[pType], bytes)
      if pType == 'int' then self.offset = self.offset + 4 end
      if pType == 'boolean' then self.offset = self.offset + 1 end
      if pType == 'long' then self.offset = self.offset + 8 end

    elseif pType == 'buffer' or pType == 'ustring' then
      local len = self:getStructLen(pType)
--      print('len=' .. len .. ',offset = ' .. self.offset)
      local bytes = string.sub(self.stream, self.offset, self.offset + len - 1)
      tmp[name] = simpleMapperVerse('>' .. structMapper[pType], bytes)
      self.offset = self.offset + len

    elseif "vector<ustring>" == pType then
--      print('offset = ' .. self.offset)
      local vectorSize = self:getIntValue()
--      print('vectorSize = ' .. vectorSize)
      local vector = {}
      for i = 1, vectorSize do
        local len = self:getStructLen('ustring')
--        print('ustring len=' .. len .. ', offset=' .. self.offset)
        -- buffer : 4byte length + content len
        local bytes = string.sub(self.stream, self.offset - 4, self.offset + len - 1)
        local ustr = simpleMapperVerse('>S', bytes)
--        for k, v in pairs(ustr) do print(k .. '=' .. v) end
        table.insert(vector, ustr[1])
        self.offset = self.offset + len
      end
      tmp[name] = vector
    elseif "data.Stat" == pType then
--      print('data.Stat offset = ' .. self.offset)
      tmp.stat = {}
      for _, dataStat in pairs(specification.data.Stat) do
        local name2 = dataStat.name
        local type2 = dataStat.type
        local len = self:getStructLen(type2)
        local bytes = string.sub(self.stream, self.offset, self.offset + len - 1)
        self.offset = self.offset + len
        tmp.stat[name2] = simpleMapperVerse('>' .. structMapper[type2], bytes)
      end
    else
      error('not support type=' .. pType)
    end
  end

  self.payload = tmp
end


return _M